package main
import (
	"fmt"
    "sync"
)
var x = 0
func increment(wg *sync.WaitGroup) {
	x = x +1
	wg.Done()	//通知该函数已结束。
}
var x1 = 0
func increment1(wg *sync.WaitGroup, m *sync.Mutex) {
	m.Lock()	//所有在 Lock 和 Unlock 之间的代码，都只能由一个 Go 协程执行，于是就可以避免竞态条件。
	x1 = x1 + 1
	m.Unlock()
	wg.Done()
}
// 使用信道处理竞态条件
var x2 = 0
func increment2(wg *sync.WaitGroup, ch chan bool) {
	ch <- true
	x2 ++
	<- ch
	wg.Done() 
}

// 25. Mutex 临界区 
func main(){

	// 含有竞态条件的程序
	var w sync.WaitGroup
	for i := 0; i < 1000; i++ {
		w.Add(1)
		go increment(&w)
	}
	w.Wait()
	fmt.Println("final value of x", x)	//输出有final value of x 978 final value of x 983 final value of x 974
	
	// 使用 Mutex
	var w1 sync.WaitGroup
	var m1 sync.Mutex
	for i := 0; i < 1000; i++ {
		w1.Add(1)
		go increment1(&w1, &m1)// &m1 ==> 传递 Mutex 的地址很重要。如果传递的是 Mutex 的值，而非地址，那么每个协程都会得到 Mutex 的一份拷贝，竞态条件还是会发生。
	}
	w1.Wait()
	fmt.Println("final value of x111", x1)

	// 使用信道处理竞态条件
	var w2 sync.WaitGroup
	ch := make(chan bool, 1)
	for i := 0; i < 1000; i++ {
		w2.Add(1)
		go increment2(&w2, ch)
	}
	w2.Wait()
	fmt.Println("final value of x222", x2)


}
